#ifndef __EPSILON_INPUT__MOUSE_H__
#define __EPSILON_INPUT__MOUSE_H__

#include <vector>

namespace Epsilon
{
  class Mouse_Listener;

  class Mouse
  {
  public:
    class Buttons
    {
    public:
      enum Button
      {
        Left,
        Primary   = Left,   /// Mac stuff
        Right,
        Secondary = Right,  /// Mac stuff
        Middle,
        Tertiary  = Middle, /// Mac stuff

        _NUMBER_OF_BUTTONS
      };
    };

    Mouse (void);

    /* Change in X since last call to getDeltaX (...) */
    inline int GetDeltaX (bool clear = false) {
      int delta = delta_x_;

      if (clear == true)
        delta_x_ = 0;

      return delta;
    }

    /* Change in Y since last call to getDeltaY (...) */
    inline int GetDeltaY (bool clear = false) {
      int delta = delta_y_;

      if (clear == true)
        delta_y_ = 0;

      return delta;
    }

    inline int GetX (void) { return x_; }
    inline int GetY (void) { return y_; }

    inline bool IsButtonDown (Buttons::Button button) {
      return buttons_ [button];
    }

    bool InstallListener   (Mouse_Listener* pListener);
    bool UninstallListener (Mouse_Listener* pListener);

//  protected:

    void PressButton   (Buttons::Button button);
    void ReleaseButton (Buttons::Button button);

    void MoveCursor      ( int  x,
                           int  y,
                           bool notify = true );
    void MoveCursorDelta ( int  delta_x,
                           int  delta_y,
                           bool notify = true );

    /* TODO: Can we find a solution that works given only absolute
               coordinates (x,y) or delta (delta_x,delta_y)? */
    void MoveCursorEx ( int x,
                        int y,
                        int delta_x,
                        int delta_y );

    void Reset (void); ///< Call this when all mouse states need to
                       ///    be reset...

  private:
    bool  buttons_ [Buttons::_NUMBER_OF_BUTTONS];
    int   x_;
    int   y_;

    int   delta_x_;
    int   delta_y_;

    /* TODO: Should we store mouse coordinates relative to a window,
               _AND_ in global coordinates?

             Right now, x_ and y_ are relative to window coordinates.
    */
//  float rel_x_;
//  float rel_y_;

    std::vector <Mouse_Listener *> listeners_;
  };

  /**
   * Mouse listeners have so many different events that it would be a real pain
   *   to make all of these functions pure virtual. Instead, all events include
   *     a stub implementation.
   *
   *   Classes designed to listen for mouse events may override one or more of
   *     the virtual functions - the return value indicates whether or not the
   *       event should be sent to additional event handlers.
   *
  **/
  class Mouse_Listener
  {
  public:
             Mouse_Listener (void) { };
    virtual ~Mouse_Listener (void) { };

    virtual bool OnButtonPress   (Mouse::Buttons::Button& button) {
      return true;
    }

    virtual bool OnButtonRelease (Mouse::Buttons::Button& button) {
      return true;
    }

    virtual bool OnMouseMove   (int       x, int       y)  { return true; };
    virtual bool OnMouseDelta  (int delta_x, int delta_y)  { return true; };
    virtual bool OnMouseScroll (int delta_z)               { return true; };

    virtual bool OnEnterWindow (void)                      { return true; };
    virtual bool OnLeaveWindow (void)                      { return true; };

  protected:
  private:
  };
};

#endif /* __EPSILON_INPUT__MOUSE_H__ */
